/*
*  Copyright(C) 2011 Koninklijke Philips Electronics N.V.,
*  All Rights Reserved.
*  This  source code and any compilation or derivative thereof is the
*  proprietary information of Koninklijke Philips Electronics N.V.
*  and is confidential in nature.
*  Under no circumstances is this software to be exposed to or placed
*  under an Open Source License of any type without the expressed
*  written permission of Koninklijke Philips Electronics N.V.
*
*          %name: %
* %date_modified: %
*       %version: %
*    %derived_by: %
*/

#ifndef _HANDY_H
#define _HANDY_H

/**************************
    Generic macro's
 **************************/
 
#define _MACRO_BEGIN do { 
#define _MACRO_END } while(0)
#define _MACRO(...) _MACRO_BEGIN __VA_ARGS__ _MACRO_END

#ifndef COUNT_OF
#define COUNT_OF(x) (sizeof(x)/sizeof((x)[0]))
#endif
#ifndef ALLOC
#define ALLOC(x) ((x *)malloc(sizeof(x)))
#endif
#ifndef ALLOC_COUNT
#define ALLOC_COUNT(x,count) ((x *)malloc(sizeof(x)*(count)))
#endif
#ifndef ALLOC_STRING
#define ALLOC_STRING(len) ((char *)malloc(len+1))
#endif

/**************************
    Debug macro's
 **************************


Debugging can be enabled using "#define _DEBUG_ENABLE 1", and asserting can be enabled with _ASSERT_ENABLE.
When debugging is disabled, both _CVCALL _CCALL will still call the function, but the result will not be checked

Examples:

#define _DEBUG_ENABLE 1 // enable debug+assert mode
#include <handy2.h>

_TRACE; // file name and number

_TFUNC; // file name,number, and function name

_TECHO("Hello World");

_TVAR(i, myIntegerVariable); // show integer variable
_TVAR(s, myStringVariable); // show string variable
_TVAR(.10s, myStringVariable); // show first 10 characters of string variable

_TLINE(_TV(i,var1);_TECHO("after allocation");); // multiple things on one line

_TFLINE(_TV(i,var1);_TECHO("after allocation");); // shows "<function>: ..."
_ENTER(_TV(i,arg1);_TV(s,arg2);) // show function and print two arguments
_LEAVE(_TV(i,result);)

_DUMPVAL(ptrToObject);           // dump memory of object
_DUMP(ptr, byteLength);           // dump memory

_ASSERT(x==0); // if x is not null, show assertion.
_ASSERT_VERBOSE(x==0, _TV(i,x)); // if x is not null, show assertion and print x.
_ASSERT_EQUAL(x,0); // if x is not null, show assertion and the values.

_CCALL(func())     // checks whether func() returns 0
_CVCALL(func(),retVal)     // checks whether func() returns retVal

*****************/

// Configuration / Porting:
#ifndef _DEBUG_STREAM
#define _DEBUG_STREAM           stderr
#endif
#ifndef _PRINT_PREFIX
#define _PRINT_PREFIX            ""
#endif
#ifndef _IF_ANSI
#define _IF_ANSI(yes,no)        yes
#endif
#ifndef _IF_AUTOFLUSH
#define _IF_AUTOFLUSH(yes,no)   yes
#endif
#ifdef _USE_STDOUT
#define _PRINTF_HEADER             printf(
#define _VPRINTF_HEADER            vprintf(
#define _FLUSH                  fflush(stdout) //;fsync(fileno(stdout))
#else
#ifndef _PRINTF_HEADER
#define _PRINTF_HEADER             fprintf(_DEBUG_STREAM,
#endif
#ifndef _VPRINTF_HEADER
#define _VPRINTF_HEADER            vfprintf(_DEBUG_STREAM,
#endif
#ifndef _FLUSH
#define _FLUSH                  fflush(_DEBUG_STREAM) //;fsync(fileno(_DEBUG_STREAM))
#endif
#endif
#ifndef _ERROR_HANDLING
#define _ERROR_HANDLING
#endif

// Implementation:
#ifndef _DEBUG_ENABLE
#ifdef DBGPRINT
#define _DEBUG_ENABLE   1
#else
#define _DEBUG_ENABLE   0
#endif
#endif
#ifndef _ASSERT_ENABLE
#if defined(ASSERT) || _DEBUG_ENABLE
#define _ASSERT_ENABLE   1
#else
#define _ASSERT_ENABLE   0
#endif
#endif
#if _ASSERT_ENABLE
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#define _IF_ASSERT(yes,no) yes
#else
#define _IF_ASSERT(yes,no) no
#endif
#if _DEBUG_ENABLE
#define _IF_DEBUG(yes,no) yes
#else
#define _IF_DEBUG(yes,no) no
#endif
#ifndef _FULL_FILE_PATHNAMES
#define _PATH2NAME(target,path) target=strrchr(path,'/'); target = target ? target : (path)
#else
#define _PATH2NAME(target,path) target=path
#endif

#define _ANSI_CMD(cmd)     _IF_ANSI("\x1b[" cmd,)
#define _ANSI_NORMAL       _ANSI_CMD("0m")
#define _ANSI_DARK(txt)   _ANSI_CMD("1;30m") txt _ANSI_NORMAL
#define _ANSI_RED(txt)    _ANSI_CMD("1;31m") txt _ANSI_NORMAL
#define _ANSI_GREEN(txt)   _ANSI_CMD("1;32m") txt _ANSI_NORMAL
#define _ANSI_YELLOW(txt)   _ANSI_CMD("1;33m") txt _ANSI_NORMAL
#define _ANSI_BLUE(txt)   _ANSI_CMD("1;34m") txt _ANSI_NORMAL
#define _ANSI_MAGENTA(txt)   _ANSI_CMD("1;35m") txt _ANSI_NORMAL
#define _ANSI_CYAN(txt)   _ANSI_CMD("1;36m") txt _ANSI_NORMAL
#define _ANSI_WHITE(txt)   _ANSI_CMD("1;37m") txt _ANSI_NORMAL
#define _ANSI_ERROR(txt)   _ANSI_RED(txt)
#define _ANSI_FAINT(txt)   _ANSI_CYAN(txt)
#define _ANSI_STRONG(txt)  _ANSI_WHITE(txt)

#if _DEBUG_ENABLE
#define _PRINTF(...)      _MACRO_BEGIN _PRINTF_HEADER _PRINT_PREFIX __VA_ARGS__); _MACRO_END
#else
#define _PRINTF(...)
#endif

#define _PRINTLF(txt,...) _IF_DEBUG(_MACRO(_PRINTF(txt "\n", ##__VA_ARGS__); _IF_AUTOFLUSH(_FLUSH,);),)
#define _PRINTTF(txt,...) _MACRO(const char *__pn; _PATH2NAME(__pn, __FILE__); _PRINTLF(txt _ANSI_FAINT(" (%s,%i)"), ##__VA_ARGS__, __pn , __LINE__); )
#define _PRINTTFF(txt,...) _PRINTTF(_ANSI_FAINT("%s: ") txt,__func__,##__VA_ARGS__)
#define _PRINTFF(txt,...) _PRINTLF(_ANSI_FAINT("%s: ") txt,__func__,##__VA_ARGS__)

#define _ECHO(str)        _PRINTTFF("%s",str)
#define _WARNING(str,...) _PRINTTFF("Warning: " _ANSI_ERROR(str),  ##__VA_ARGS__)
#define _ERROR(str,...)   _MACRO(_PRINTTFF("Error: " _ANSI_ERROR(str), ##__VA_ARGS__); _ERROR_HANDLING;)
#define _TRACE            _PRINTTFF("")
#define _TENTER()         _ECHO( "Entering" )
#define _TLEAVE()         _ECHO( "Leaving" )
#define _TVARFSTR(t,v)         "%s = " #t ": %" #t " "
#define _TVAR(t,v)   	        _PRINTTFF(_TVARFSTR(t,v), #v, v)
#define _TVAR2(t,v,t2,v2)   	  _PRINTTFF(_TVARFSTR(t,v) _TVARFSTR(t2,v2), #v, v, #v2, v2)
#define _TVAR3(t,v,t2,v2,t3,v3) _PRINTTFF(_TVARFSTR(t,v) _TVARFSTR(t2,v2) _TVARFSTR(t3,v3), #v, v, #v2, v2, #v3, v3)
#define _TVAR4(t,v,t2,v2,t3,v3,t4,v4) _PRINTTFF(_TVARFSTR(t,v) _TVARFSTR(t2,v2) _TVARFSTR(t3,v3) _TVARFSTR(t4,v4), #v, v, #v2, v2, #v3, v3, #v4, v4)
#define _DUMP(str,v,l)        _IF_DEBUG(_MACRO( \
	int __i; unsigned char *__v=(unsigned char *)(v); unsigned char *__ev=__v+(l); _ECHO(str "{");\
	while( __v<__ev)\
	{	char lineBuf[80], *p=lineBuf;\
		for(__i=0; __i<16 && __v <__ev; __i++) { sprintf(p, " %.2x", *(__v++)); p+=3;} \
		__v-=__i; do { sprintf(p, "   "); p+=3;} while(__i++<16);  \
		for(__i=0; __i<16 && __v <__ev; __i++) { char __c=*(__v++); sprintf(p++,"%c", __c<32?'.':__c); } _PRINTF("%s\n",lineBuf); \
	} _PRINTLF("}"); ),)
#define _TDUMP(v,l)        _DUMP(#v "= ", v,l)
#define _TDUMPVAL(v)        _TDUMP(v, sizeof(*(v)))
#define _CVCALL_VERBOSE(x,retval,action) _IF_ASSERT(_MACRO(int __err=(int)(x); if (__err!=retval) { _PRINTTFF("\"%s\" " _ANSI_ERROR("returned: 0x%x"), #x,__err); action; _ERROR_HANDLING; }),(void)(x))
#define _CCALL_VERBOSE(x,action)         _CVCALL_VERBOSE(x,0,action)
#define _ASSERT_VERBOSE(x,action)        _IF_ASSERT(_MACRO(if ((x)==0) { _ECHO("\"" #x "\" " _ANSI_ERROR("asserted.") );  action; _ERROR_HANDLING; }),)
#define _CVCALL(x,retval)  _CVCALL_VERBOSE(x,retval,)
#define _CCALL(x)          _CCALL_VERBOSE(x,)
#define _ASSERT(x)         _ASSERT_VERBOSE(x,)
#define _ASSERT_EQUAL(x,y) _IF_ASSERT(_MACRO(if ((x)!=(y)) _PRINTTFF("\"" #x "!=" #y "\" " _ANSI_ERROR("asserted:") " 0x%lx != 0x%lx", (long)(x),(long)(y)); ),)
#define _DIV_BY_ZERO       _IF_ASSERT(_MACRO(int p=3; _PRINTF("%i\n",p/(p-3));),)

#endif
